package com.mzj.saas.commons.util;

import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.charset.Charset;

public class ByteUtils
{
	private static final byte[] EMPTY_BYTES = new byte[0] ;
	private static final ThreadLocal<ByteBuffer> convertBufferHolder = new ThreadLocal<ByteBuffer>() ;

	private static final ByteBuffer getBuffer( int size )
	{
		ByteBuffer buf = convertBufferHolder.get() ;
		if( buf == null )
		{
			buf = ByteBuffer.allocate( 16 ) ;
			convertBufferHolder.set( buf );
		}
		
		buf.clear() ;
		return buf ;
	}
	
	public static byte[] short2bytes( short value )
	{
		ByteBuffer buffer = getBuffer(2) ;
		buffer.putShort(value) ;
		buffer.flip() ;
		return readBytes(buffer) ;
	}
	
	public static byte[] int2bytes( int value )
	{
		ByteBuffer buffer = getBuffer(4) ;
		buffer.putInt(value) ;
		buffer.flip() ;
		return readBytes(buffer) ;
	}
	
	public static byte[] long2bytes( long value )
	{
		ByteBuffer buffer = getBuffer(8) ;
		buffer.putLong(value) ;
		buffer.flip() ;
		return readBytes(buffer) ;
	}
	
	public static byte[] float2bytes( float value )
	{
		ByteBuffer buffer = getBuffer(8) ;
		buffer.putFloat(value) ;
		buffer.flip() ;
		return readBytes(buffer) ;
	}
	
	public static byte[] double2bytes( double value )
	{
		ByteBuffer buffer = getBuffer(8) ;
		buffer.putDouble(value) ;
		buffer.flip() ;
		return readBytes(buffer) ;
	}
	
	public static void short2bytes( short value, byte[] dest )
	{
		for (int i=1; i >= 0; i--) 
		{
			dest[i] = (byte) (value & 0xff);
			value >>>= 8;
		}
	}

	public static void short2bytes( short value, byte[] dest, int from )
	{
		for (int i=from+1; i >= from; i--) 
		{
			dest[i] = (byte) (value & 0xff);
			value >>>= 8;
		}
	}

	public static void int2bytes( int value, byte[] dest )
	{
		for (int i=3; i >= 0; i--) 
		{
			dest[i] = (byte) (value & 0xff);
			value >>>= 8;
		}
	}

	public static void int2bytes( int value, byte[] dest, int from )
	{
		for (int i=from+3; i >= from; i--) 
		{
			dest[i] = (byte) (value & 0xff);
			value >>>= 8;
		}
	}

	public static void long2bytes( long value, byte[] dest )
	{
		for (int i=7; i >= 0; i--) 
		{
			dest[i] = (byte) (value & 0xff);
			value >>>= 8;
		}
	}

	public static void long2bytes( long value, byte[] dest, int from )
	{
		for (int i=from+7; i >= from; i--) 
		{
			dest[i] = (byte) (value & 0xff);
			value >>>= 8;
		}
	}

	public static void float2bytes( float value, byte[] dest )
	{
		ByteBuffer buf = ByteBuffer.wrap( dest ) ;
		buf.putFloat( value ) ;
	}

	public static void float2bytes( float value, byte[] dest, int from )
	{
		ByteBuffer buf = ByteBuffer.wrap( dest,from, 4 ) ;
		buf.putFloat( value ) ;
	}

	public static void double2bytes( double value, byte[] dest )
	{
		ByteBuffer buf = ByteBuffer.wrap( dest ) ;
		buf.putDouble( value ) ;
	}

	public static void double2bytes( double value, byte[] dest, int from )
	{
		ByteBuffer buf = ByteBuffer.wrap( dest, from, 8 ) ;
		buf.putDouble( value ) ;
	}
	
    public static byte[] str2bytes( String value )
	{
		if( value==null )
			return null ;
		else
			return value.getBytes() ;
	}

    public static byte[] str2bytes( String value , String encoding )
	{
		if( value==null )
			return null ;
		else
		{
			try
			{
				return value.getBytes( encoding ) ;
			}
			catch( UnsupportedEncodingException err )
			{
				err.printStackTrace() ;
				return value.getBytes() ;
			}
		}
	}

    public static byte[] str2bytes( String value , Charset encoding )
	{
		if( value==null )
			return null ;
		else
			return value.getBytes( encoding ) ;
	}
	
	public static byte[] str2bytes( String value , int length )
	{
		byte[] arr = value==null?null:str2bytes( value ) ;
		return scalebytes( arr, length ) ;
	}
	
	public static byte[] str2bytes( String value , int length, byte endOfByte )
	{
		byte[] arr = value==null?null:str2bytes( value ) ;
		return scalebytes( arr, length, endOfByte ) ;
	}
	
	public static byte[] str2bytes( String value , String encoding , int length )
	{
		byte[] arr = value==null?null:str2bytes( value,encoding ) ;
		return scalebytes( arr, length ) ;
	}
	
	public static byte[] str2bytes( String value , String encoding , int length, byte endOfByte )
	{
		byte[] arr = value==null?null:str2bytes( value,encoding ) ;
		return scalebytes( arr, length, endOfByte ) ;
	}
	
	public static byte[] str2bytes( String value , Charset encoding , int length )
	{
		byte[] arr = value==null?null:str2bytes( value,encoding ) ;
		return scalebytes( arr, length ) ;
	}
	
	public static byte[] str2bytes( String value , Charset encoding , int length, byte endOfByte )
	{
		byte[] arr = value==null?null:str2bytes( value,encoding ) ;
		return scalebytes( arr, length, endOfByte ) ;
	}
	
	public static byte[] scalebytes( byte[] src, int length )
	{
		if( length == 0 )
			return EMPTY_BYTES ;
		
		byte[] arr = new byte[length] ;
		int copylen = src==null?0:Math.min( length, src.length ) ;
		if( copylen > 0 )
			System.arraycopy( src, 0, arr, 0, copylen ) ;
		
		return arr ;
	}
	
	public static byte[] scalebytes( byte[] src, int length, byte endOfByte )
	{
		if( length == 0 )
			return EMPTY_BYTES ;
		
		byte[] arr = new byte[length] ;
		int copylen = src==null?0:Math.min( length, src.length ) ;
		if( copylen > 0 )
			System.arraycopy( src, 0, arr, 0, copylen ) ;
		
		if( endOfByte != 0x0 )
		{
			for( int i=copylen; i<length; i++ )
				arr[i] = endOfByte ;
		}
		
		return arr ;
	}
	
	public static void fill( ByteBuffer buffer, byte padding, int num )
	{
		int count = Math.min( num, buffer.remaining() ) ;
		for( int i=0; i<count; i++ )
			buffer.put( padding ) ;
	}
	
	public static void putString( ByteBuffer buffer, String value, int length )
	{
		putString( buffer, value, length, (byte)0x0 ) ;
	}
	
	public static void putString( ByteBuffer buffer, String value, int length, byte endOfByte )
	{
		byte[] arr = value==null?null:str2bytes( value ) ;
		int copylen = Math.min( arr==null?0:arr.length, length ) ;
		if( copylen > 0 )
			buffer.put( arr, 0, copylen ) ;
		
		for( int i=copylen; i<length; i++ )
			buffer.put( endOfByte ) ;
	}
	
	public static void putString( ByteBuffer buffer, String value, String encoding , int length )
	{
		putString( buffer, value, encoding, length, (byte)0x0 ) ;
	}

	public static void putString( ByteBuffer buffer, String value, String encoding , int length, byte endOfByte )
	{
		byte[] arr = value==null?null:str2bytes( value,encoding ) ;
		int copylen = Math.min( arr==null?0:arr.length, length ) ;
		if( copylen > 0 )
			buffer.put( arr, 0, copylen ) ;
		
		for( int i=copylen; i<length; i++ )
			buffer.put( endOfByte ) ;
	}

	public static void putString( ByteBuffer buffer, String value, Charset encoding , int length )
	{
		putString( buffer, value, encoding, length, (byte)0x0 ) ;
	}
	
	public static void putString( ByteBuffer buffer, String value, Charset encoding , int length, byte endOfByte )
	{
		byte[] arr = value==null?null:str2bytes( value,encoding ) ;
		int copylen = Math.min( arr==null?0:arr.length, length ) ;
		if( copylen > 0 )
			buffer.put( arr, 0, copylen ) ;
		
		for( int i=copylen; i<length; i++ )
			buffer.put( endOfByte ) ;
	}
	
	public static byte[] ushort2bytes( int value )
	{
		byte[] bts = new byte[2];
		for (int i=bts.length-1; i >= 0; i--) 
		{
			bts[i] = (byte) (value & 0xff);
			value >>>= 8;
		}
		return bts;
	}
	
	public static byte[] uint2bytes( long value )
	{
		byte[] bts = new byte[4];
		for (int i=bts.length-1; i >= 0; i--) 
		{
			bts[i] = (byte) (value & 0xff);
			value >>>= 8;
		}
		return bts;
	}
	
	public static void ushort2bytes( int value, byte[] dest )
	{
		for (int i=dest.length-1; i >= 0; i--) 
		{
			dest[i] = (byte) (value & 0xff);
			value >>>= 8;
		}
	}
	
	public static void uint2bytes( long value, byte[] dest )
	{
		for (int i=dest.length-1; i >= 0; i--) 
		{
			dest[i] = (byte) (value & 0xff);
			value >>>= 8;
		}
	}

	public static short bytes2short(byte[] obj )
	{
		ByteBuffer buffer = ByteBuffer.wrap(obj) ;
		return buffer.getShort() ;
	}
	
	public static short bytes2short(byte[] obj, int from )
	{
		ByteBuffer buffer = ByteBuffer.wrap(obj,from,obj.length-from) ;
		return buffer.getShort() ;
	}
	
	public static int bytes2int(byte[] obj)
	{
		ByteBuffer buffer = ByteBuffer.wrap(obj) ;
		return buffer.getInt() ;
	}
	
	public static int bytes2int(byte[] obj, int from )
	{
		ByteBuffer buffer = ByteBuffer.wrap(obj,from,obj.length-from) ;
		return buffer.getInt() ;
	}
	
	public static long bytes2long(byte[] obj)
	{
		ByteBuffer buffer = ByteBuffer.wrap(obj) ;
		return buffer.getLong() ;
	}
	
	public static long bytes2long(byte[] obj, int from )
	{
		ByteBuffer buffer = ByteBuffer.wrap(obj,from,obj.length-from) ;
		return buffer.getLong() ;
	}
	
	public static float bytes2float(byte[] obj)
	{
		ByteBuffer buffer = ByteBuffer.wrap(obj) ;
		return buffer.getFloat() ;
	}
	
	public static float bytes2float(byte[] obj, int from )
	{
		ByteBuffer buffer = ByteBuffer.wrap(obj,from,obj.length-from) ;
		return buffer.getFloat() ;
	}
	
	public static double bytes2double(byte[] obj)
	{
		ByteBuffer buffer = ByteBuffer.wrap(obj) ;
		return buffer.getDouble() ;
	}
	
	public static double bytes2double(byte[] obj, int from )
	{
		ByteBuffer buffer = ByteBuffer.wrap(obj,from,obj.length-from) ;
		return buffer.getDouble() ;
	}
	
	public static int bytes2ushort(byte[] obj)
	{
		ByteBuffer buffer = ByteBuffer.wrap(obj) ;
		return readUnsignedShort(buffer) ;
	}
	
	public static int bytes2ushort(byte[] obj, int from )
	{
		ByteBuffer buffer = ByteBuffer.wrap(obj,from,obj.length-from) ;
		return readUnsignedShort(buffer) ;
	}
	
	public static long bytes2uint(byte[] obj)
	{
		ByteBuffer buffer = ByteBuffer.wrap(obj) ;
		return readUnsignedInt(buffer) ;
	}
	
	public static long bytes2uint(byte[] obj, int from )
	{
		ByteBuffer buffer = ByteBuffer.wrap(obj,from,obj.length-from) ;
		return readUnsignedInt(buffer) ;
	}

	public static String bytes2str(byte[] obj)
	{
		if( obj == null || obj.length <= 0 )
			return null ;
		
    	int begin = 0 ;
        for(;begin < obj.length && obj[begin] == 0x0 ; begin++ ) ;
        
        int end = begin ;
        for(;end < obj.length && obj[end] != 0x0 ; end++ ) ;

        int len = end-begin ;
        if( len <= 0 )
        	return null ;

		return new String( obj,begin,len );
	}
	
	public static String bytes2str(byte[] obj , String encoding )
	{
		if( obj == null || obj.length <= 0 )
			return null ;
		
    	int begin = 0 ;
        for(;begin < obj.length && obj[begin] == 0x0 ; begin++ ) ;
        
        int end = begin ;
        for(;end < obj.length && obj[end] != 0x0 ; end++ ) ;
		
        int len = end-begin ;
        if( len <= 0 )
        	return null ;
        
        return bytes2str( obj, encoding, begin, len) ;
	}
	
	public static String bytes2str(byte[] obj , String encoding, int offset, int length )
	{
		try
		{
			return new String( obj, offset, length , encoding ) ;
		}
		catch (UnsupportedEncodingException err)
		{
			err.printStackTrace() ;
			return new String( obj ) ;
		}
	}

	public static String readString(ByteBuffer buffer, int length )
	{
		int r = length<0?buffer.remaining():Math.min(length, buffer.remaining()); 
		if( r == 0 )
			return null ;

		byte[] tmp = new byte[r] ;
		buffer.get(tmp) ;
		return bytes2str( tmp ) ;
	}
	
	public static String readString(ByteBuffer buffer, String encoding , int length )
	{
		int r = length<0?buffer.remaining():Math.min(length, buffer.remaining()); 
		if( r == 0 )
			return null ;

		byte[] tmp = new byte[r] ;
		buffer.get(tmp) ;
		return bytes2str( tmp , encoding ) ;
	}

	public static String readString(ByteBuffer buffer, byte endOfByte )
	{
		return readString( buffer, -1, endOfByte ) ;
	}
	
	public static String readString(ByteBuffer buffer, int length, byte endOfByte )
	{
		int r = length<0?buffer.remaining():Math.min(length, buffer.remaining()); 
		if( r == 0 )
			return null ;
		
		int pos = buffer.position() ;
		
		byte[] arr = new byte[r] ;
		int len = arr.length ;
		for( int i=0; i<r; i++ )
		{
			byte b = buffer.get() ;
			if( b==endOfByte || b == 0 )
			{
				len = i ;
				break ;
			}
			else
				arr[i] = b ;
		}
		
		buffer.position( pos+r ) ;
		if( len <= 0 )
			return null ;
		
		return new String( arr, 0 , len ) ;
	}
	
	public static String readString(ByteBuffer buffer, String encoding , int length, byte endOfByte )
	{
		int r = length<0?buffer.remaining():Math.min(length, buffer.remaining()); 
		if( r == 0 )
			return null ;

		byte[] arr = new byte[r] ;
		int len = arr.length ;
		for( int i=0; i<r; i++ )
		{
			byte b = buffer.get() ;
			if( b==endOfByte || b == 0 )
			{
				len = i ;
				break ;
			}
			else
				arr[i] = b ;
		}
		
		return bytes2str( arr , encoding, 0, len ) ;
	}

	public static String readString(ByteBuffer buffer )
	{
		return readString( buffer, -1 ) ;
	}
	
	public static String readString(ByteBuffer buffer, String encoding )
	{
		return readString( buffer, encoding, -1 ) ;
	}
	
	public static byte[] readBytes(ByteBuffer buffer, int length )
	{
		if( !buffer.hasRemaining() )
			return EMPTY_BYTES ;
		byte[] bts = new byte[length<0?buffer.remaining():length] ;
		buffer.get( bts ) ;
		
		return bts ;
	}
	
	public static void skipBytes(ByteBuffer buffer )
	{
		skipBytes( buffer, -1 ) ;
	}
	
	public static void skipBytes(ByteBuffer buffer, int length )
	{
		if( !buffer.hasRemaining() )
			return ;
		buffer.position( buffer.position()+(length<0?buffer.remaining():length) ) ;
	}
	
	public static byte[] readBytes(ByteBuffer buffer )
	{
		return readBytes( buffer, -1 ) ;
	}
	
	public static byte[] getFixedLengthBytes(byte[] origin, int length )
	{
		if( origin == null )
			return length==0?origin:new byte[length] ;
		if( origin.length == length )
			return origin ;
		byte[] bts = new byte[length] ;
		System.arraycopy(origin, 0, bts, 0, Math.min(origin.length, bts.length ) ) ;
			
		return bts ;
	}

    public static byte[] trim( byte[] _message )
    {
        byte[] validByte = null ;
        if( null != _message && _message.length > 0 )
        {
        	int begin = 0 ;
            for(;begin < _message.length && _message[begin] == 0x0 ; begin++ ) ;
            
            int end = begin ;
            for(;end < _message.length && _message[end] != 0x0 ; end++ ) ;
            
			if (begin==0 && end >= _message.length)
				return _message;
			else
            {
                validByte = new byte[end-begin] ;
                System.arraycopy(_message,begin,validByte,0,end-begin) ;
            }
        }
        return validByte ;
    }

    public static byte[] trim( byte[] _message, byte paddingByte )
    {
        byte[] validByte = null ;
        if( null != _message && _message.length > 0 )
        {
        	int begin = 0 ;
            for(;begin < _message.length && _message[begin] == paddingByte ; begin++ ) ;
            
            int end = begin ;
            for(;end < _message.length && _message[end] != paddingByte ; end++ ) ;
            
			if (begin==0 && end >= _message.length)
				return _message;
			else
            {
                validByte = new byte[end-begin] ;
                System.arraycopy(_message,begin,validByte,0,end-begin) ;
            }
        }
        return validByte ;
    }
    
    public static int readUnsignedShort( ByteBuffer buffer )
    {
		byte[] bts = new byte[2];
		buffer.get(bts);
		
		if( buffer.order() == ByteOrder.LITTLE_ENDIAN )
			return makeUnsignedShort(bts[1], bts[0]) ;
		else
			return makeUnsignedShort(bts[0], bts[1]) ;
    }
    
    public static long readUnsignedInt( ByteBuffer buffer )
    {
		byte[] bts = new byte[4];
		buffer.get(bts);
		
		if( buffer.order() == ByteOrder.LITTLE_ENDIAN )
			return makeUnsignedInt(bts[3], bts[2], bts[1], bts[0]) ;
		else
			return makeUnsignedInt(bts[0], bts[1], bts[2], bts[3]) ;
    }
    
	private static final String[] binaryStrs = new String[256] ;
	
	static
	{
		StringBuilder strbuf = new StringBuilder() ;
		for( int i=0; i<256; i++ )
		{
			String str = Integer.toBinaryString(i) ;
			for( int j=str.length(); j<8; j++ )
				strbuf.append( "0" ) ;
			strbuf.append(str) ;
			binaryStrs[i] = strbuf.toString() ;
			strbuf.delete(0, strbuf.length() ) ;
		}
	}
    
	public static final String byte2str( byte bval )
	{
		return binaryStrs[bval&0xff] ;
	}
	
	public static final String bytes2str( ByteBuffer buffer )
	{
		StringBuilder strbuf = new StringBuilder() ;
		while( buffer.hasRemaining() )
			strbuf.append( byte2str( buffer.get() ) ) ;
		
		return strbuf.toString() ;
	}
	
	public static final byte char1(char x){	return (byte) (x >> 8); }
	public static final byte char0(char x){	return (byte) (x); }
	
	public static final byte short1(short x){ return (byte) (x >> 8); }
	public static final byte short0(short x){ return (byte) (x); }
	
    public static final byte int3(int x) { return (byte)(x >> 24); }
    public static final byte int2(int x) { return (byte)(x >> 16); }
    public static final byte int1(int x) { return (byte)(x >>  8); }
    public static final byte int0(int x) { return (byte)(x      ); }
    
    public static final byte long7(long x) { return (byte)(x >> 56); }
    public static final byte long6(long x) { return (byte)(x >> 48); }
    public static final byte long5(long x) { return (byte)(x >> 40); }
    public static final byte long4(long x) { return (byte)(x >> 32); }
    public static final byte long3(long x) { return (byte)(x >> 24); }
    public static final byte long2(long x) { return (byte)(x >> 16); }
    public static final byte long1(long x) { return (byte)(x >>  8); }
    public static final byte long0(long x) { return (byte)(x      ); }
	
	public static final char makeChar(byte b1, byte b0){ return (char) ((b1 << 8) | (b0 & 0xff)); }
	public static final short makeShort(byte b1, byte b0){ return (short) ((b1 << 8) | (b0 & 0xff)); }
	public static final int makeInt(byte b3, byte b2, byte b1, byte b0){ return (((b3) << 24) | ((b2 & 0xff) << 16) | ((b1 & 0xff) << 8) | ((b0 & 0xff)));}
	public static final long makeLong(byte b7, byte b6, byte b5, byte b4, byte b3, byte b2, byte b1, byte b0){return ((((long) b7) << 56) | (((long) b6 & 0xff) << 48) | (((long) b5 & 0xff) << 40) | (((long) b4 & 0xff) << 32) | (((long) b3 & 0xff) << 24) | (((long) b2 & 0xff) << 16) | (((long) b1 & 0xff) << 8) | (((long) b0 & 0xff)));	}
	public static final int makeUnsignedShort(byte b1, byte b0){ return (int) (((b1&0xff) << 8) | (b0&0xff)); }
	public static final long makeUnsignedInt(byte b3, byte b2, byte b1, byte b0){ return (long) ((((long) b3 & 0xff) << 24) | (((long) b2 & 0xff) << 16) | (((long) b1 & 0xff) << 8) | (((long) b0 & 0xff)));	}
    
	public static final char makeCharL(byte b1, byte b0){ return (char) ((b0 << 8) | (b1 & 0xff)); }
	public static final short makeShortL(byte b1, byte b0){ return (short) ((b0 << 8) | (b1 & 0xff)); }
	public static final int makeIntL(byte b3, byte b2, byte b1, byte b0){ return (((b0) << 24) | ((b1 & 0xff) << 16) | ((b2 & 0xff) << 8) | ((b3 & 0xff)));}
	public static final long makeLongL(byte b7, byte b6, byte b5, byte b4, byte b3, byte b2, byte b1, byte b0){return ((((long) b0) << 56) | (((long) b1 & 0xff) << 48) | (((long) b2 & 0xff) << 40) | (((long) b3 & 0xff) << 32) | (((long) b4 & 0xff) << 24) | (((long) b5 & 0xff) << 16) | (((long) b6 & 0xff) << 8) | (((long) b7 & 0xff)));	}
	public static final int makeUnsignedShortL(byte b1, byte b0){ return (int) (((b0&0xff) << 8) | (b1&0xff)); }
	public static final long makeUnsignedIntL(byte b3, byte b2, byte b1, byte b0){ return (long) ((((long) b0 & 0xff) << 24) | (((long) b1 & 0xff) << 16) | (((long) b2 & 0xff) << 8) | (((long) b3 & 0xff)));	}
	

}
